﻿namespace Sai.Core.Rules
{
	using System;
	using System.Collections.Generic;
	using System.IO;
	using System.Reflection;
    using System.Threading.Tasks;
	
	public class RulePluginReflectionFinder : IRulePluginFinder
	{
		private readonly string fileFilter = "*Rules.dll";

		private string folder;

		public RulePluginReflectionFinder(string folder)
		{
			this.folder = folder;
		}

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFile")]
        public int Find(IRulePluginRepository repo)
		{
			foreach (string path in Directory.GetFiles(this.folder, this.fileFilter))
			{
				Assembly assembly = Assembly.LoadFile(path);

				this.LoadRules(assembly, repo);
			}

            return repo.TypeRules.Count + repo.AssemblyRules.Count;
		}

		private static bool MatchNameFilter(Type type, object interfaceName)
		{
			string match = interfaceName as string;

			return type.ToString() == match;
		}

		private List<Type> FindTypesImplementingInterface(Type requiredInterface, Assembly assembly)
		{
			List<Type> implementingTypes = new List<Type>();

			string interfaceName = requiredInterface.FullName;

			foreach (Type candidateType in assembly.GetTypes())
			{
				if (candidateType.IsAbstract || candidateType.IsInterface)
				{
					continue;
				}

				if (candidateType.FindInterfaces(new TypeFilter(MatchNameFilter), interfaceName).Length > 0)
				{
					implementingTypes.Add(candidateType);
				}
			}

			return implementingTypes;
		}

		private int LoadRules(Assembly assembly, IRulePluginRepository repo)
		{
			List<Type> typeRuleTypes = this.FindTypesImplementingInterface(typeof(ITypeRule), assembly);

			foreach (Type ruleType in typeRuleTypes)
			{
                ITypeRule rule = (ITypeRule)Activator.CreateInstance(ruleType);
				repo.Add(rule);
			}

            List<Type> assemblyRuleTypes = this.FindTypesImplementingInterface(typeof(IAssemblyRule), assembly);

			foreach (Type ruleType in assemblyRuleTypes)
			{
				IAssemblyRule rule = (IAssemblyRule)Activator.CreateInstance(ruleType);
				repo.Add(rule);
			}

            return repo.TypeRules.Count + repo.AssemblyRules.Count;
		}
	}
}
